#include "Context.h"
#include "Tkgles.h"
#include <math.h>


/*==================================================================================================
*	DESCRIPTIONS:
*	Following function set the CURRENT drawing color
*
*	PARAMETER:
*		-
*	RETURNS:
*		-
==================================================================================================*/
GL_API void GL_APIENTRY glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
	
	Context * context = getContext();
	
	if (context){
		context->Color.red = red;
		context->Color.green = green;
		context->Color.blue = blue;
		context->Color.alpha = alpha;
	}
}

GL_API void GL_APIENTRY glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz) {

	Context * context = getContext();
	
	if (context) {
		context->Normal.x = nx;
		context->Normal.y = ny;
		context->Normal.z = nz;
	}

}

GL_API void GL_APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) {

	Context * context = getContext();
	
	GLint index = target - GL_TEXTURE0;
	
	if (context) {
		context->TextureCoords[index].s = s;
		context->TextureCoords[index].t = t;
		context->TextureCoords[index].r = r;
		context->TextureCoords[index].q = q;
	}
}

GL_API void GL_APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) {

	Context * context = getContext();
	
	if (context) {

		context->VertexArray.pointer = (GLvoid *)pointer;
		context->VertexArray.type = type;//byte, short, fixed, float
		context->VertexArray.stride = stride;
		context->VertexArray.size = size; //2,3,4

	}
	
}

GL_API void GL_APIENTRY glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer) {
	
	Context * context = getContext();
	
	if (context) {
		context->NormalArray.pointer = (GLvoid *)pointer;
		context->NormalArray.type = type; //byte, short, fixed, float
		context->NormalArray.stride = stride;
		//context->NormalArray.size = 3; //Optional
	}
	
}

GL_API void GL_APIENTRY glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) {

	Context * context = getContext();
	
	if (context) {
		context->ColorArray.pointer = (GLvoid *)pointer;
		context->ColorArray.size = size; //always 4
		context->ColorArray.type = type; //ubyte, fixed, float
		context->ColorArray.stride = stride;
		
	}
	
}

GL_API void GL_APIENTRY glPointSizePointerOES (GLenum type, GLsizei stride, const GLvoid *pointer) {

	Context * context = getContext();
	
	if (context) {
		context->PointSizePointer.pointer = (GLvoid *)pointer;
		context->PointSizePointer.type = type; //fixed, float
		context->PointSizePointer.stride = stride;
		context->PointSizePointer.size = 1;
	}
}

GL_API void GL_APIENTRY glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) {

	Context * context = getContext();
	
	if (context) {
		context->TexCoordPointer.pointer = (GLvoid *)pointer;
		context->TexCoordPointer.type = type; //byte, short, fixed, float
		context->TexCoordPointer.size = size; //2,3,4
		context->TexCoordPointer.stride = stride;
	}
	
}

GL_API void GL_APIENTRY glEnableClientState (GLenum array) {

	Context * context = getContext();
	
	if (context) {
		switch (array) {
			case GL_TEXTURE_COORD_ARRAY:
				context->TexCoordArrayEnabled[context->ClientActiveTexture - GL_TEXTURE0] = GL_TRUE;
				break;

			case GL_COLOR_ARRAY:
				context->ColorArrayEnabled = GL_TRUE;
				break;

			case GL_NORMAL_ARRAY:
				context->NormalArrayEnabled = GL_TRUE;
				break;

			case GL_VERTEX_ARRAY:
				context->VertexArrayEnabled = GL_TRUE;
				break;

			case GL_POINT_SIZE_ARRAY_OES:
				context->PointSizeArrayEnabled = GL_TRUE;
				break;

			default:
				RecordError(GL_INVALID_ENUM);
				break;
		}
	}
	
}

GL_API void GL_APIENTRY glDisableClientState (GLenum array) {

	Context * context = getContext();
	
	if (context) {
		switch (array) {
			case GL_TEXTURE_COORD_ARRAY:
				context->TexCoordArrayEnabled[context->ClientActiveTexture] = GL_FALSE;
				break;

			case GL_COLOR_ARRAY:
				context->ColorArrayEnabled = GL_FALSE;
				break;

			case GL_NORMAL_ARRAY:
				context->NormalArrayEnabled = GL_FALSE;
				break;

			case GL_VERTEX_ARRAY:
				context->VertexArrayEnabled = GL_FALSE;
				break;

			case GL_POINT_SIZE_ARRAY_OES:
				context->PointSizeArrayEnabled = GL_FALSE;
				break;

			default:
				RecordError(GL_INVALID_ENUM);
				break;
		}
	}
	
}

GL_API void GL_APIENTRY glClientActiveTexture (GLenum texture) {

	Context * context = getContext();
	
	if (context) {
		context->ClientActiveTexture = texture - GL_TEXTURE0;
	}
	
}

GL_API void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count) {

	//printf("glDrawArrays executed\n");
	Context * context = getContext();
	int i,ith;
	
	if (count < 0) {
		RecordError(GL_INVALID_VALUE);
		return;
	}

	if (!(context->VertexArrayEnabled)) {
		RecordError(GL_INVALID_OPERATION);
		return;
	}
	//Something goes here..
	
	switch ( mode ){
		case GL_POINTS:
			for (i = first; i < count; i++){
				Transform ( i );
				DrawPoint(i);
			}
			break;
		
		case GL_LINE_STRIP:
			for (i = first; i < count; i++){
				Transform (i);
				if (i > 0){
					DrawLine(i-1,i);
				} 
			}
			break;
		
		case GL_LINE_LOOP:
			for (i = first; i < count; i++){
				Transform (i);
				if (i > 0){
					DrawLine(i-1,i);
				} 
			}
			DrawLine(count - 1,0);
			break;
		
		case GL_LINES:
		break;
		
		case GL_TRIANGLE_STRIP:
            ith = 0;
            while (ith < count - 2){
    			for (i = ith; i < ith + 3; i++){
        			Transform(i);
            	}
                DrawTriangle(ith);
                //printf("Drawing %dth triangle\n",ith);
                ith++;
            }
		break;
		
		case GL_TRIANGLE_FAN:
		break;
		
		case GL_TRIANGLES:
			//check the legality of first and count
			for (i = first; i < first + count; i++){
				Transform(i);
			}
			DrawTriangle(first);
			break;
		
	}

}

GL_API void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height) {

	Context * context = getContext();
	
	if (context) {
		context->ViewPort.ox = x + width/2; //x coordinate of center point of viewport
		context->ViewPort.oy = y + height/2; //y coordinate of center point of viewport
		context->ViewPort.px = width; //width of viewport
		context->ViewPort.py = height; //height of viewport
	}

}

GL_API void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar) {

	Context * context = getContext();
	//zNear and zFar are clamped into [0,1]
	if (context) {
		context->DepthRange.zNear = zNear;
		context->DepthRange.zFar = zFar;
	}

}

GL_API void GL_APIENTRY glMatrixMode (GLenum mode) {

	Context * context = getContext();
	
	if (context) {
		if ( mode == GL_MODELVIEW ){
			context->Matrix.pCurrentMatrix = &(context->Matrix.ModelViewStack[context->Matrix.ModelViewStackPointer]);
		} else if (mode == GL_PROJECTION){
			context->Matrix.pCurrentMatrix = &(context->Matrix.ProjectionStack[context->Matrix.ProjectionStackPointer]);
		} else if (mode == GL_TEXTURE){
			context->Matrix.pCurrentMatrix = &(context->Matrix.TextureStack[context->ActiveTexture - GL_TEXTURE0][context->Matrix.TextureStackPointer[context->ActiveTexture - GL_TEXTURE0]]);
		}
	}
}

GL_API void GL_APIENTRY glLoadMatrixf (const GLfloat *m) {

	MatrixImport((GLfloat*)m);	

}

GL_API void GL_APIENTRY glMultMatrixf (const GLfloat *m) {

	Context * context = getContext();

	if (context) {
		MatrixImportM((GLfloat*)m);
	}
	
}

GL_API void GL_APIENTRY glLoadIdentity (void) {
	
	Context * context = getContext();
	//replace the current active matrix
	
	if (context) {
		MatrixImport((GLfloat*)&(context->Matrix.IdentityMatrix));
	}
	
}

GL_API void GL_APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z){

	GLfloat _x,_y,_z, R[9],_cos,_sin;

	INT i = 0;
	_x = x/(sqrt(x*x+y*y+z*z));
	_y = y/(sqrt(x*x+y*y+z*z));
	_z = z/(sqrt(x*x+y*y+z*z));
	
	GLfloat U[9] = {_x*_x, _x*_y, _x*_z,
                   _y*_x, _y*_y, _y*_z,
                   _z*_x, _z*_y, _z*_z};
		 
	GLfloat S[9] = {0, _z, -_y,
						 -_z, 0, _x,
						 _y, -_x, 0};
	
	GLfloat I[9] = {1, 0, 0,
		 0, 1, 0,
		 0, 0, 1};

	_cos = cos(angle);
	_sin = sin(angle);
	
	for (i = 0; i < 9; i++){
		R[i] = U[i] + _cos*(I[i] - U[i]) + _sin*S[i];
	}

	GLfloat newMt[16] = {R[0], R[1], R[2], 0,
			 					R[3], R[4], R[5], 0,
			 					R[6], R[7], R[8], 0,
			  					 0,     0,   0,   1};

	MatrixImportM(newMt);

}

GL_API void GL_APIENTRY glTranslatef (GLfloat x, GLfloat y, GLfloat z) {

	GLfloat newMt[16] = {1,0,0,0,
							 	0,1,0,0,
							 	0,0,1,0,
							 	x,y,z,1};

	MatrixImportM(newMt);

}

GL_API void GL_APIENTRY glScalef (GLfloat x, GLfloat y, GLfloat z){

	GLfloat newMt[16] = {x,0,0,0,
							 	0,y,0,0,
							 	0,0,z,0,
							 	0,0,0,1};


	MatrixImportM(newMt);

}

GL_API void GL_APIENTRY glFrustumf (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar){

	GLfloat newMt[16] = { 2*zNear/(right-left),		 	0				  	,0	,	 	0		,
										0				 , 2*zNear/(top-bottom)	,0	,		0		,
						(right+left)/(right-left),(top+bottom)/(top-bottom),-(zFar+zNear)/(zFar-zNear),-1,
										0				 ,			0					,-2*zFar*zNear/(zFar-zNear),		0						};

	MatrixImportM(newMt);


}

GL_API void GL_APIENTRY glOrthof (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar){

	GLfloat newMt[16] = { 2/(right-left),			0		,			0		,		0		,
										0		,2/(top-bottom),			0		,		0		,
										0		,			0		,-2/(zFar-zNear),		0		,
						-(right+left)/(right-left),-(top+bottom)/(top-bottom),-(zFar+zNear)/(zFar-zNear),				1					};


	MatrixImportM(newMt);


}

GL_API void GL_APIENTRY glEnable (GLenum cap) {
	ToggleState(cap, GL_TRUE );
}

GL_API void GL_APIENTRY glDisable (GLenum cap) {
	ToggleState(cap, GL_FALSE );
}


GL_API void GL_APIENTRY glClipPlanef (GLenum plane, const GLfloat *equation){

	Context * context =  getContext();
	if (context){
		context->ClipPlane[plane - GL_CLIP_PLANE0].p1 = equation[0];
		context->ClipPlane[plane - GL_CLIP_PLANE0].p2 = equation[1];
		context->ClipPlane[plane - GL_CLIP_PLANE0].p3 = equation[2];
		context->ClipPlane[plane - GL_CLIP_PLANE0].p4 = equation[3];
	}
}

GL_API void GL_APIENTRY glPointSize (GLfloat size){

	Context * context  = getContext();

	if (context){
		context->PointSize = size;
	}
}
